/*
 * $Id: BulletmlUtil.java,v 1.3 2001/06/03 00:19:12 ChoK Exp $
 *
 * Copyright 2001 Kenta Cho. All rights reserved.
 */
package jp.gr.java_conf.abagames.bulletml_demo;

import java.util.Hashtable;


/**
 * Utility class for BulletML.
 *
 * @version $Revision: 1.3 $
 */
public class BulletmlUtil
{
    private static Hashtable<String, Expression> expressions = new Hashtable<String, Expression>();
    
    private static char[] expChr;
    private static float rank = 0.5f;

    public static int getIntValue(String v, float[] prms)
    {
        return (int) evalExpression(v, prms);
    }

    public static float getFloatValue(String v, float[] prms)
    {
        return evalExpression(v, prms);
    }

    public static void setRank(float rk)
    {
        rank = rk;
    }

    public static float getRank()
    {
        return rank;
    }

    private static void evalFloatValue(Expression ep, int stIdx, int lgt, float sign)
    {
        if (expChr[stIdx] == '$')
        {
            String label = new String(expChr, stIdx + 1, lgt - 1);

            if (label.equals("rand"))
            {
                //return rnd.nextFloat();
                ep.push(0, Expression.STACK_RAND);
            }
            else if (label.equals("rank"))
            {
                //return rank;
                ep.push(0, Expression.STACK_RANK);
            }
            else
            {
                int idx;

                try
                {
                    idx = new Integer(label).intValue() - 1;
                }
                catch (NumberFormatException e)
                {
                    //return 0;
                    ep.push(0, Expression.STACK_NUM);

                    return;
                }

                //return prms[idx];
                ep.push(0, Expression.STACK_VARIABLE + idx);
            }
        }
        else
        {
            try
            {
                //return new Float(new String(expChr, stIdx, lgt)).floatValue();
                ep.push(new Float(new String(expChr, stIdx, lgt)).floatValue() * sign, Expression.STACK_NUM);
            }
            catch (NumberFormatException e)
            {
                //return 0;
                ep.push(0, Expression.STACK_NUM);
            }
        }
    }

    private static void evalExpPart(Expression ep, int stIdx, int edIdx)
    {
        int[] op = new int[] { -1, -1 };

        while ((expChr[stIdx] == '(') && (expChr[edIdx - 1] == ')'))
        {
            stIdx++;
            edIdx--;
        }

        for (int i = edIdx - 1; i >= stIdx; i--)
        {
            char c = expChr[i];

            if (c == ')')
            {
                do
                {
                    i--;
                }
                while (expChr[i] != '(');
            }
            else if ((op[0] < 0) && ((c == '*') || (c == '/') || (c == '%')))
            {
                op[0] = i;
            }
            else if ((c == '+') || (c == '-'))
            {
                op[1] = i;

                break;
            }
        }

        if (op[1] < 0)
        {
            if (op[0] < 0)
            {
                //return evalFloatValue(stIdx, edIdx-stIdx);
                evalFloatValue(ep, stIdx, edIdx - stIdx, 1);
            }
            else
            {
                switch (expChr[op[0]])
                {
                case '*':

                    //return evalExpPart(stIdx, op[0]) * evalExpPart(op[0]+1, edIdx);
                    evalExpPart(ep, stIdx, op[0]);
                    evalExpPart(ep, op[0] + 1, edIdx);
                    ep.setOperator(Expression.MULTIPLE);

                    break;

                case '/':

                    //return evalExpPart(stIdx, op[0]) / evalExpPart(op[0]+1, edIdx);
                    evalExpPart(ep, stIdx, op[0]);
                    evalExpPart(ep, op[0] + 1, edIdx);
                    ep.setOperator(Expression.DIVISION);

                    break;

                case '%':

                    //return evalExpPart(stIdx, op[0]) % evalExpPart(op[0]+1, edIdx);
                    evalExpPart(ep, stIdx, op[0]);
                    evalExpPart(ep, op[0] + 1, edIdx);
                    ep.setOperator(Expression.MODULO);

                    break;
                }
            }
        }
        else
        {
            if (op[1] == stIdx)
            {
                switch (expChr[op[1]])
                {
                case '-':

                    //return -evalFloatValue(stIdx+1, edIdx-stIdx-1);
                    evalFloatValue(ep, stIdx + 1, edIdx - stIdx - 1, -1);

                    break;

                case '+':

                    //return  evalFloatValue(stIdx+1, edIdx-stIdx-1);
                    evalFloatValue(ep, stIdx + 1, edIdx - stIdx - 1, 1);

                    break;
                }
            }
            else
            {
                switch (expChr[op[1]])
                {
                case '+':

                    //return evalExpPart(stIdx, op[1]) + evalExpPart(op[1]+1, edIdx);
                    evalExpPart(ep, stIdx, op[1]);
                    evalExpPart(ep, op[1] + 1, edIdx);
                    ep.setOperator(Expression.PLUS);

                    break;

                case '-':

                    //return evalExpPart(stIdx, op[1]) - evalExpPart(op[1]+1, edIdx);
                    evalExpPart(ep, stIdx, op[1]);
                    evalExpPart(ep, op[1] + 1, edIdx);
                    ep.setOperator(Expression.MINUS);

                    break;
                }
            }
        }
    }

    public static float evalExpression(String exp, float[] p)
    {
        Expression ep = (Expression) expressions.get(exp);

        if (ep == null)
        {
            expChr = new char[exp.length()];

            int ecIdx = 0;
            boolean skip = false;
            StringBuffer buf = new StringBuffer(exp);
            int depth = 0;
            boolean balance = true;
            char ch;

            for (int i = 0; i < buf.length(); i++)
            {
                ch = buf.charAt(i);

                switch (ch)
                {
                case ' ':
                case '\n':
                    skip = true;

                    break;

                case ')':
                    depth--;

                    if (depth < 0)
                    {
                        balance = false;
                    }

                    break;

                case '(':
                    depth++;

                    break;
                }

                if (skip)
                {
                    skip = false;
                }
                else
                {
                    expChr[ecIdx] = ch;
                    ecIdx++;
                }
            }

            if ((depth != 0) || !balance)
            {
                return 0;
            }

            ep = new Expression();
            evalExpPart(ep, 0, ecIdx);
            expressions.put(exp, ep);
        }

        return ep.calc(p);
    }
}
